package com.cancan.demo.apidebouncing.req;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

import com.cancan.demo.apidebouncing.annotation.RequestLock;
import com.cancan.demo.apidebouncing.exception.BusinessException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.util.StringUtils;
 
/**
 * @description 缓存实现
 */
@Aspect
@Configuration
@Order(2)
public class RedisRequestLockAspect {

    private static final Logger logger = LoggerFactory.getLogger(RedisRequestLockAspect.class);

    private final StringRedisTemplate stringRedisTemplate;
 
    @Autowired
    public RedisRequestLockAspect(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
 
    @Around("execution(public * * (..)) && @annotation(com.cancan.demo.apidebouncing.annotation.RequestLock)")
    public Object interceptor(ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        RequestLock requestLock = method.getAnnotation(RequestLock.class);
        if (StringUtils.isEmpty(requestLock.prefix())) {
            throw new BusinessException("重复提交前缀不能为空");
        }
        //获取自定义key
        final String lockKey = RequestKeyGenerator.getLockKey(joinPoint);
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(lockKey))) {
            throw new BusinessException("您的操作太快了,请稍后重试");
        }else{
            // 使用RedisCallback接口执行set命令，设置锁键；设置额外选项：过期时间和SET_IF_ABSENT选项
            stringRedisTemplate.execute(
                    (RedisCallback<Boolean>)connection -> connection.set(lockKey.getBytes(), new byte[0],
                            Expiration.from(requestLock.expire(), requestLock.timeUnit()),
                            RedisStringCommands.SetOption.SET_IF_ABSENT));
        }
        try {
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            throw new BusinessException("系统异常:"+ throwable.getMessage());
        }
    }

    public Boolean acquireLock(String lockKey, long expire, TimeUnit timeUnit) {
        try {
            return stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
                StringRedisConnection stringRedisConnection = (StringRedisConnection) connection;
                return stringRedisConnection.set(lockKey, "", Expiration.from(expire, timeUnit), RedisStringCommands.SetOption.SET_IF_ABSENT);
            });
        } catch (Exception e) {
            logger.error("Failed to acquire lock for key: {}", lockKey, e);
            return false;
        }
    }
}